// program to implement a one pass assembler for the SIC machine
// Input : source file
// Output: object file, list file

#include "gclass_0.h"


struct fwdref_node
  {
	char loc[5];
	fwdref_node *link;
  };
const unsigned int max_symbols=50;
class symboltable
{
 private:

  struct symtabrec
  {
	char symbol[10];
	char value[5];
	char flag;
	fwdref_node *start;
  } table[max_symbols];
  char symbol[10],loc[5],value[5];
  int index,num_symbols;

 public:
  symboltable();
  int search_symbol();
  char *_value(char *_symbol,char *locctr);
  int add(char *_symbol,char *_value);
  void add_fwdreflist();
  void undefined_symbols(listfile &lst,objectfile& obj);
  void newsymtabentry();
  void qsort(int left,int right);
  void exchange(int i,int j);
  fwdref_node *start() { return table[index].start; }
  char *_value() { return table[index].value; }
};

symboltable::symboltable()
{
 num_symbols=0;
 for(register int i=0;i<max_symbols;i++)
  {
	table[i].flag='*';
	table[i].start=NULL;
  }
}

int symboltable::search_symbol()
{
 int beg=0,end=num_symbols-1;
 while(beg<=end)
 {
	int mid=(beg+end)/2;
	int cmp=strcmp(table[mid].symbol,symbol);
	if (cmp==0)
		 {
		  index=mid;
		  return 1;
		 }
	else
	 if (cmp<1)
		  beg=mid+1;
	 else
		  end=mid-1;
 }
 return 0;
}

char * symboltable::_value(char *_symbol,char *locctr)
{
 strcpy(symbol,_symbol);
 strcpy(loc,locctr);
 strcpy(value,"0000");
 if (search_symbol())
		{
		  if (table[index].flag!='*')
				 strcpy(value,table[index].value);
		  else
				 add_fwdreflist();
		}
 else
		newsymtabentry();
 return value;
}

void symboltable::add_fwdreflist()
{
 fwdref_node *ptr=table[index].start,*_new;
 while(ptr->link!=NULL) ptr=ptr->link;
 _new=new fwdref_node;
 ptr->link=_new;
 _new->link=NULL;
 strcpy(_new->loc,loc);
}

void symboltable::newsymtabentry()
{
 strcpy(table[num_symbols].symbol,symbol);
 table[num_symbols].flag='*';
 table[num_symbols].start=new fwdref_node;
 table[num_symbols].start->link=NULL;
 strcpy(table[num_symbols].start->loc,loc);
 num_symbols++;
 qsort(0,num_symbols-1);
}

void symboltable::qsort(int left,int right)
{
 if (left>=right) return;
 char key[10];
 strcpy(key,table[left].symbol);
 int i=left+1,j=right;
 int done=0;
 while(!done)
 {
 while(strcmp(table[i].symbol,key)<0){ i++; }
 while(strcmp(table[j].symbol,key)>0){ j--; }
 if (i<j)
		{
		  exchange(i,j);
		  i++;
		  j--;
		}
  else
		{
		 exchange(left,j);
		 done=1;
		}
 }
 qsort(left,j-1);
 qsort(j+1,right);
}

void symboltable::exchange(int i,int j)
{
 symtabrec temp=table[i];
 table[i]=table[j];
 table[j]=temp;
}

int symboltable::add(char *_symbol,char *_value)
{
 strcpy(symbol,_symbol);
 strcpy(value,_value);
 int ret;
 if (search_symbol())
	 {
	  if (table[index].flag!='*')
			ret=0;
	  else
			{
			 strcpy(table[index].value,value);
			 table[index].flag='#';
			 ret=2;
			}
	 }
 else
	 {
	  strcpy(table[num_symbols].symbol,symbol);
	  strcpy(table[num_symbols].value,value);
	  table[num_symbols].flag='#';
	  ret=1;
	  num_symbols++;
	  qsort(0,num_symbols-1);
	 }
  return ret;
}

void symboltable::undefined_symbols(listfile &lst,objectfile &obj)
{
 fwdref_node *ptr,*next;
 char line[30];
 for(register int i=0;i<num_symbols;i++)
 {
  if (table[i].flag!='#')
	  {
		ostrstream s(line,30);
		s<<"Undefined symbol <"<<table[i].symbol<<">"<<ends;
		ptr=table[i].start;
		while(ptr!=NULL)
		 {
		  next=ptr->link;
		  istrstream i(ptr->loc,5);
		  int j;
		  i>>hex>>j;
		  j--;
		  ostrstream o(ptr->loc,5);
		  o<<hex<<j<<ends;
		  obj.adjustnumber(ptr->loc,4);
		  lst.errormessage(ptr->loc,line);
		  delete ptr;
		  ptr=next;
		 }
		}
 }
}

class codegenerator
{
 private:
  sourcefile src;
  objectfile obj;
  listfile   lst;
  scanner  scan;
  symboltable symtab;
  opcodetable optab;
  char locctr[5];
  char code[3],value[7];
  char start_address[5];
  char prog_length[7];
  char instrn[20];
  char line[80];

 public:
  codegenerator(char *source,char *object,char *list);
  void assemble();
  void nextline();
  void inc_locctr(int cnt);
  void process_fwdreflist();
  void generate_objectcode();
  int val(char *op);
  void convert_word();
  void convert_byte();
  void extract(char *temp1,char *temp2);
  void calculate_length();
};

codegenerator::codegenerator(char *source,char *object,char *list):
					src(source),obj(object),lst(list,source)
{ }

void codegenerator::nextline()
{
 src.readline();
 strcpy(line,src.getline());
 scan.setline(line);
 scan.scan();
 if (!scan.comment())
 {
 ostrstream o(line,80);
 o<<setiosflags(ios::left)<<setw(10)<<scan.label<<"  "<<setw(10)<<scan.opcode
  <<"  "<<setw(10)<<scan.operand<<ends;
 if (scan.indexmode)
	  {
		 line[strlen(line)-2]='\0';
		 strcat(line,",X");
	  }
 }
}

void codegenerator::assemble()
{
 nextline();
 char name[7],temploc[5];
 int processed=0;
 if (strcmp(scan.opcode,"START")==0)
	 {
	  strcpy(start_address,scan.operand);
	  obj.adjustnumber(start_address,4);
	  strcpy(locctr,start_address);
	  lst.writelisting(locctr,line," ");
	  strcpy(name,scan.label);
	  symtab.add(name,locctr);
	  nextline();
	 }
 else
	{
	 strcpy(start_address,"0000");
	 strcpy(locctr,start_address);
	 name[0]='\0';
	}
 obj.write_header(name,start_address);
 obj.initialise_textrecord(locctr);

 while(strcmp(scan.opcode,"END")!=0)
 {
  strcpy(temploc,locctr);
  if (!scan.comment())
	 {
	  if (scan.label[0]!='\0')
			{
			  int ret;
			  if (!(ret=symtab.add(scan.label,locctr)))
					lst.errormessage(locctr,"Duplicate symbol definition");
			  if (ret==2)
				  {
					 process_fwdreflist();
					 processed=1;
					 obj.initialise_textrecord(locctr);
				  }
			 }
	  if (optab.isopcode(scan.opcode))
		 {
		  if (scan.operand[0]=='\0')
			 {
			  strcpy(value,"0000");
			  if (strcmp(scan.opcode,"RSUB")!=0)
				 lst.errormessage(locctr,"Operand expected");
			 }
		  else
			 {
				char temp[5];
				strcpy(temp,locctr);
				inc_locctr(1);
				strcpy(value,symtab._value(scan.operand,locctr));
				strcpy(locctr,temp);
			  }
		  if (processed) {
								obj.initialise_textrecord(locctr);
								processed=0;
							  }
		  generate_objectcode();
		  obj.add_to_textrecord(instrn,locctr);
		  inc_locctr(3);
		 }
	  else
		 {
		  if (strcmp(scan.opcode,"RESW")==0)
				 {
				  inc_locctr(val(scan.operand)*3);
				  instrn[0]='\0';
				  obj.write_textrecord();
				  obj.initialise_textrecord(locctr);
				 }
		  else
			if (strcmp(scan.opcode,"RESB")==0)
			  {
				inc_locctr(val(scan.operand));
				instrn[0]='\0';
				obj.write_textrecord();
				obj.initialise_textrecord(locctr);
			  }
			else
			 if (strcmp(scan.opcode,"WORD")==0)
			  {
				if (processed) {
								obj.initialise_textrecord(locctr);
								processed=0;
							  }
				convert_word();
				obj.add_to_textrecord(instrn,locctr);
				inc_locctr(3);
			  }
			 else
			  if (strcmp(scan.opcode,"BYTE")==0)
				{
				 if (processed) {
								obj.initialise_textrecord(locctr);
								processed=0;
							  }

				 convert_byte();
				 obj.add_to_textrecord(instrn,locctr);
				 inc_locctr(strlen(instrn)/2);
				}
			  else
				{
				 char msg[30];
				 ostrstream o(msg,30);
				 o<<"Invalid opcode <"<<scan.opcode<<">"<<ends;
				 lst.errormessage(locctr,msg);
				 instrn[0]='\0';
				}
		 }
	 lst.writelisting(temploc,line,instrn);
	 }
	 else
		lst.writelisting("    ",line," ");
	 nextline();
 }
 obj.write_textrecord();
 if (scan.operand[0]=='\0')
	  strcpy(value,start_address);
 else
	 strcpy(value,symtab._value(scan.operand,locctr));
 obj.write_endrecord(value);
 calculate_length();
 obj.write_proglength(prog_length);
 lst.writelisting("    ",line," ");
 symtab.undefined_symbols(lst,obj);
 lst.writeerrors();
}

void codegenerator::inc_locctr(int cnt)
{
 istrstream s(locctr,5);
 int loc;
 s>>hex>>loc;
 loc+=cnt;
 ostrstream o(locctr,5);
 o<<hex<<setiosflags(ios::uppercase)<<loc<<ends;
 obj.adjustnumber(locctr,4);
}

void codegenerator::process_fwdreflist()
{
 obj.write_textrecord();
 fwdref_node *ptr=symtab.start(),*next;
 while(ptr!=NULL)
  {
	obj.initialise_textrecord(ptr->loc);
	strcpy(instrn,symtab._value());
	obj.add_to_textrecord(instrn,locctr);
	obj.write_textrecord();
	next=ptr->link;
	delete ptr;
	ptr=next;
  }
}

void codegenerator::generate_objectcode()
{
 long int j;
 strcpy(code,optab.value());
 if (scan.indexmode==1)
  {
	istrstream i(value,5);
	i>>hex>>j;
	j|=0x8000;
	ostrstream o(value,5);
	o<<hex<<setiosflags(ios::uppercase)<<j<<ends;
  }
 strcpy(instrn,code);
 strcat(instrn,value);
}

int codegenerator::val(char *op)
{
 istrstream i(op,10);
 int j;
 i>>j;
 return j;
}

void codegenerator::calculate_length()
{
 istrstream i1(start_address,5);
 istrstream i2(locctr,5);
 int s,l;
 i1>>hex>>s;
 i2>>hex>>l;
 ostrstream o(prog_length,7);
 o<<hex<<setiosflags(ios::uppercase)<<l-s<<ends;
 obj.adjustnumber(prog_length,6);
}
void codegenerator::convert_word()
{
 strcpy(instrn,scan.operand);
 istrstream i(instrn,20);
 int j;
 i>>dec>>j;
 ostrstream o(instrn,20);
 o<<hex<<setiosflags(ios::uppercase)<<j<<ends;
 obj.adjustnumber(instrn,6);
}

void codegenerator::convert_byte()
{
 char temp1[10],temp2[10];
 strcpy(temp1,scan.operand);
 extract(temp2,temp1);
 ostrstream o(instrn,20);
 if (temp1[0]=='X')
	 o<<temp2<<ends;
 else
	{
	 for(int k=0;k<strlen(temp2);k++)
		o<<hex<<setiosflags(ios::uppercase)<<toascii(temp2[k]);
	 o<<ends;
	}
}

void codegenerator::extract(char *temp2,char *temp1)
{
 int k=0;
 for(int i=2;i<strlen(temp1);i++)
 {
  if (temp1[i]=='\'')
		break;
  temp2[k++]=temp1[i];
 }
 temp2[k]='\0';
}

void main()
{
 char src[30];

 cout<<"File: "; cin>>src;
 codegenerator c(src,"out.obj","out.lst");
 c.assemble();
}












































































